package com.yanqu.xiuxian.crosssystem.manger.elementalbonds.logic;

import com.yanqu.xiuxian.config.elementalbonds.enums.ElementalBondsBlockEffectType;
import com.yanqu.xiuxian.config.elementalbonds.enums.ElementalBondsBlockType;
import com.yanqu.xiuxian.config.elementalbonds.enums.ElementalBondsColorType;
import com.yanqu.xiuxian.config.elementalbonds.enums.ElementalBondsOperateType;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.data.ElementalBondsGameData;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.data.ElementalBondsPlayerData;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.ElementalBondsBlock;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.ElementalBondsBlockEffect;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.ElementalBondsBomb;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.entity.eliminate.ElementalBondsEliminateTip;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.enums.ElementalBondsCardType;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.param.ElementalBondsMoveParam;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.result.ElementalBondsOperateResult;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.result.ElementalBondsOperateRoundResult;
import com.yanqu.xiuxian.crosssystem.manger.elementalbonds.result.ElementalBondsOperateStageResult;
import com.yanqu.xiuxian.server.protocol.GameErrorCode;

import java.util.*;
import java.util.stream.Collectors;

public class ElementalBondsEliminateLogic {

    public static ElementalBondsEliminateTip getEliminateTip(ElementalBondsGameData elementalBondsGameData) {
        List<List<ElementalBondsBlock>> blockList = elementalBondsGameData.getBlockList();

        // 检查组合炸弹
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                ElementalBondsBlock fromElementalBondsBlock = blockList.get(y).get(x);
                if (ElementalBondsBlockLogic.combBombPart(fromElementalBondsBlock)) {
                    if (ElementalBondsBlockLogic.exist(blockList, x + 1, y) && ElementalBondsBlockLogic.combBombPart(blockList.get(y).get(x + 1))) {
                        return new ElementalBondsEliminateTip(fromElementalBondsBlock, blockList.get(y).get(x + 1));
                    }
                    if (ElementalBondsBlockLogic.exist(blockList, x, y + 1) && ElementalBondsBlockLogic.combBombPart(blockList.get(y + 1).get(x))) {
                        return new ElementalBondsEliminateTip(fromElementalBondsBlock, blockList.get(y + 1).get(x));
                    }
                }
            }
        }
        // 复制一份出来
        blockList = elementalBondsGameData.clone().getBlockList();

        // 遍历每个方块尝试交换
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {

                // 跟右边交换
                int rightX = x + 1;
                if (ElementalBondsBlockLogic.exist(blockList, rightX, y)) {
                    ElementalBondsBlockLogic.exchange(blockList, x, y, rightX, y, null);
                    if (canSameColorEliminate(blockList, blockList.get(y).get(x))
                            || canSameColorEliminate(blockList, blockList.get(y).get(rightX))) {
                        ElementalBondsBlockLogic.exchange(blockList, x, y, rightX, y, null);
                        return new ElementalBondsEliminateTip(blockList.get(y).get(x), blockList.get(y).get(rightX));
                    }
                    ElementalBondsBlockLogic.exchange(blockList, x, y, rightX, y, null);
                }

                // 跟上边交换
                int upY = y + 1;
                if (ElementalBondsBlockLogic.exist(blockList, x, upY)) {
                    ElementalBondsBlockLogic.exchange(blockList, x, y, x, upY, null);
                    if (canSameColorEliminate(blockList, blockList.get(y).get(x))
                            || canSameColorEliminate(blockList, blockList.get(upY).get(x))) {
                        ElementalBondsBlockLogic.exchange(blockList, x, y, x, upY, null);
                        return new ElementalBondsEliminateTip(blockList.get(y).get(x), blockList.get(upY).get(x));
                    }
                    ElementalBondsBlockLogic.exchange(blockList, x, y, x, upY, null);
                }

            }
        }

        return null;
    }

    public static int checkEliminate(ElementalBondsGameData elementalBondsGameData, ElementalBondsMoveParam elementalBondsMoveParam,
                                     ElementalBondsOperateResult elementalBondsOperateResult, Set<ElementalBondsBlock> elementalBondsBlockSet) {
        if (elementalBondsBlockSet != null) {
            // 检查技能提前消除方块
            if (!elementalBondsBlockSet.isEmpty()) {
                return GameErrorCode.E_SUCCESS_VALUE;
            }
        }

        List<List<ElementalBondsBlock>> blockList = elementalBondsGameData.getBlockList();

        if (elementalBondsMoveParam != null) {
            // 检查移动组合炸弹
            ElementalBondsBlock fromElementalBondsBlock = blockList.get(elementalBondsMoveParam.getFromY()).get(elementalBondsMoveParam.getFromX());
            ElementalBondsBlock toElementalBondsBlock = blockList.get(elementalBondsMoveParam.getToY()).get(elementalBondsMoveParam.getToX());
            if (ElementalBondsBlockLogic.combBombPart(fromElementalBondsBlock) && ElementalBondsBlockLogic.combBombPart(toElementalBondsBlock)) {
                return GameErrorCode.E_SUCCESS_VALUE;
            }
            // 检查同色方块消除
            ElementalBondsBlockLogic.exchange(blockList, elementalBondsMoveParam.getFromX(), elementalBondsMoveParam.getFromY(), elementalBondsMoveParam.getToX(), elementalBondsMoveParam.getToY(), null);
            ElementalBondsSceneLogic.scene_08_fixColor(elementalBondsGameData, elementalBondsOperateResult);
            for (List<ElementalBondsBlock> elementalBondsBlockList : blockList) {
                for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockList) {
                    if (canSameColorEliminate(blockList, elementalBondsBlock)) {
                        ElementalBondsBlockLogic.exchange(blockList, elementalBondsMoveParam.getFromX(), elementalBondsMoveParam.getFromY(), elementalBondsMoveParam.getToX(), elementalBondsMoveParam.getToY(), null);
                        return GameErrorCode.E_SUCCESS_VALUE;
                    }
                }
            }
            ElementalBondsBlockLogic.exchange(blockList, elementalBondsMoveParam.getFromX(), elementalBondsMoveParam.getFromY(), elementalBondsMoveParam.getToX(), elementalBondsMoveParam.getToY(), null);
        }

        return GameErrorCode.E_ELEMENTAL_BONDS_BLOCK_CAN_NOT_ELIMINATE;
    }

    public static void eliminate(ElementalBondsGameData elementalBondsGameData, ElementalBondsPlayerData elementalBondsPlayerData,
                                 ElementalBondsMoveParam elementalBondsMoveParam, Set<ElementalBondsBlock> elementalBondsBlockSet, ElementalBondsOperateResult elementalBondsOperateResult,
                                 Random random, int guideDropNo, int roomRound, int turnPlayer) {

        ElementalBondsBlockLogic.removeNoBaseFourBigType(elementalBondsBlockSet);

        ElementalBondsOperateType elementalBondsOperateType = elementalBondsOperateResult.getElementalBondsOperateType();

        Set<ElementalBondsBlock> lastRoundDropSet = new HashSet<>();
        for (int round = 1; round <= 100; round++) {

            ElementalBondsSceneLogic.scene_08_fixColor(elementalBondsGameData, elementalBondsOperateResult);

            List<List<ElementalBondsBlock>> blockList = elementalBondsGameData.getBlockList();

            // 本轮参数
            Set<ElementalBondsBlock> roundEliminateSet = new HashSet<>();
            List<ElementalBondsBomb> roundBombList = new ArrayList<>();
            Set<ElementalBondsBlock> nextStageBombSet = new HashSet<>();

            ElementalBondsOperateRoundResult elementalBondsOperateRoundResult = new ElementalBondsOperateRoundResult();
            for (int stage = 1; stage <= 100; stage++) {

                // 本阶段参数
                Set<ElementalBondsBlock> backupEliminateSet = new HashSet<>(roundEliminateSet);
                Set<ElementalBondsBomb> backupBombList = new HashSet<>(roundBombList);
                Set<ElementalBondsBlock> backupNextStageBombSet = new HashSet<>(nextStageBombSet);
                ElementalBondsOperateStageResult elementalBondsOperateStageResult = new ElementalBondsOperateStageResult();

                // 上一阶段炸弹
                if (round > 1 || stage > 1) {
                    ElementalBondsBombLogic.handleBombBoom(blockList, nextStageBombSet, roundEliminateSet, roundBombList);
                }

                // 组合炸弹
                if (round == 1 && stage == 1 && elementalBondsOperateType == ElementalBondsOperateType.MOVE) {
                    ElementalBondsBombLogic.handleCombBombBoom(blockList, elementalBondsMoveParam, roundEliminateSet, roundBombList);
                }

                // 同色消除 + 快速爆炸
                if (stage == 1) {
                    handleSameColorEliminate(blockList, roundEliminateSet);
                    ElementalBondsSceneLogic.afterSameColorEliminate(elementalBondsGameData, roundEliminateSet, elementalBondsOperateStageResult, elementalBondsOperateType);
                    ElementalBondsBombLogic.fastBoom(elementalBondsGameData, elementalBondsOperateType, roundEliminateSet, roundBombList);
                }

                // 触发技能消除
                if (round == 1 && stage == 1) {
                    ElementalBondsCardLogic.card_13_move(elementalBondsGameData, elementalBondsPlayerData, elementalBondsOperateType, roundEliminateSet, roundBombList); // 这个技能比较特殊，移动触发消除才能触发
                    roundEliminateSet.addAll(elementalBondsBlockSet);
                    ElementalBondsBombLogic.fastBoom(elementalBondsGameData, elementalBondsOperateType, roundEliminateSet, roundBombList);
                }

                // 检查下边框消除
                eliminateFrame(elementalBondsGameData, roundEliminateSet, elementalBondsOperateType);

                // 添加下阶段爆炸炸弹
                nextStageBombSet = ElementalBondsBlockLogic.getCanBoomBlock(roundBombList, roundEliminateSet);

                // 记录阶段结果
                elementalBondsOperateStageResult.getBombList().addAll(roundBombList.stream().skip(backupBombList.size()).collect(Collectors.toList()));
                elementalBondsOperateStageResult.getEliminateBlockList().addAll(roundEliminateSet.stream()
                        .filter(elementalBondsBlock -> !backupEliminateSet.contains(elementalBondsBlock))
                        .collect(Collectors.toList()));
                elementalBondsOperateStageResult.getEliminateBlockList().addAll(backupNextStageBombSet);
                elementalBondsOperateStageResult.getEliminateBlockList().removeAll(nextStageBombSet);
                elementalBondsOperateRoundResult.addStageResult(elementalBondsOperateStageResult);
                if (nextStageBombSet.isEmpty()) {
                    break;
                }
            }
            // 本轮没有可以消除的
            if (!roundEliminateSet.isEmpty()) {

                // 1.消除的格子置空
                setEliminateEmpty(blockList, roundEliminateSet);

                // 2.生成炸弹
                ElementalBondsBombLogic.createBomb(elementalBondsMoveParam, elementalBondsOperateResult, random, elementalBondsGameData, lastRoundDropSet, round, roundEliminateSet, elementalBondsOperateRoundResult);

                // 3.掉落方块
                lastRoundDropSet = ElementalBondsBlockLogic.drop(blockList, elementalBondsGameData, random, guideDropNo, roomRound, turnPlayer);

                // 4.设置结果
                elementalBondsOperateRoundResult.setElementalBondsGameData(elementalBondsGameData.clone());
                elementalBondsOperateResult.addElementalBondsOperateRoundResult(elementalBondsOperateRoundResult);

                // 5.检查重排
                ElementalBondsBlockLogic.checkRearrange(elementalBondsGameData, elementalBondsOperateResult, random);

            } else {
                break;
            }
        }
    }

    public static void bigEliminate(ElementalBondsGameData elementalBondsGameData, ElementalBondsPlayerData elementalBondsPlayerData,
                                    ElementalBondsMoveParam elementalBondsMoveParam, Set<ElementalBondsBlock> elementalBondsBlockSet, ElementalBondsOperateResult elementalBondsOperateResult,
                                    Random random, int guideDropNo, int roomRound, int turnPlayer) {
        // 正常消除一次
        eliminate(elementalBondsGameData, elementalBondsPlayerData, elementalBondsMoveParam, elementalBondsBlockSet, elementalBondsOperateResult,
                random, guideDropNo, roomRound, turnPlayer);

        // 消除结束操作
        ElementalBondsSceneLogic.eliminateEnd(elementalBondsGameData, elementalBondsPlayerData, elementalBondsOperateResult,
                random, roomRound, turnPlayer);

        // 盘面稳定后技能
        int cardId = elementalBondsPlayerData.getElementalBondsCardData().getCardId();
        if (cardId == ElementalBondsCardType.CARD_13.getType()) {
            ElementalBondsCardLogic.card_13_moveEnd(elementalBondsGameData, elementalBondsPlayerData, elementalBondsOperateResult,
                    elementalBondsOperateResult.getElementalBondsOperateType(), random);
        } else if (cardId == ElementalBondsCardType.CARD_22.getType()) {
            ElementalBondsCardLogic.card_22_eliminateEnd(elementalBondsGameData, elementalBondsPlayerData, elementalBondsOperateResult,
                    elementalBondsOperateResult.getElementalBondsOperateType(), random);
        } else if (cardId == ElementalBondsCardType.CARD_23.getType()) {
            ElementalBondsCardLogic.card_23_moveEnd(elementalBondsGameData, elementalBondsOperateResult,
                    elementalBondsOperateResult.getElementalBondsOperateType(), random);
        }
    }

    private static void setEliminateEmpty(List<List<ElementalBondsBlock>> blockList, Set<ElementalBondsBlock> elementalBondsBlockSet) {
        for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockSet) {
            blockList.get(elementalBondsBlock.getY()).set(elementalBondsBlock.getX(), null);
        }
    }

    public static void handleSameColorEliminate(List<List<ElementalBondsBlock>> blockList, Set<ElementalBondsBlock> elementalBondsBlockSet) {
        for (List<ElementalBondsBlock> elementalBondsBlockList : blockList) {
            for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockList) {
                if (elementalBondsBlockSet.contains(elementalBondsBlock)) {
                    continue;
                }
                if (!canSameColorEliminate(blockList, elementalBondsBlock)) {
                    continue;
                }
                elementalBondsBlockSet.add(elementalBondsBlock);
            }
        }
    }

    public static boolean canSameColorEliminate(List<List<ElementalBondsBlock>> blockList, ElementalBondsBlock elementalBondsBlock) {

        if (elementalBondsBlock.getColor() == ElementalBondsColorType.NONE.getType()) {
            return false;
        }

        int x = elementalBondsBlock.getX();
        int y = elementalBondsBlock.getY();
        int color = elementalBondsBlock.getColor();
        int num = 0; // 连续同色的数量

        // 1. 左到右
        for (int tmpX = x - 2; tmpX <= x + 2; tmpX++) {
            if (ElementalBondsBlockLogic.exist(blockList, tmpX, y) && color == blockList.get(y).get(tmpX).getColor()) {
                num++;
            } else {
                num = 0;
            }
            if (num >= 3) {
                return true;
            }
        }
        // 2. 下到上
        for (int tmpY = y - 2; tmpY <= y + 2; tmpY++) {
            if (ElementalBondsBlockLogic.exist(blockList, x, tmpY) && color == blockList.get(tmpY).get(x).getColor()) {
                num++;
            } else {
                num = 0;
            }
            if (num >= 3) {
                return true;
            }
        }
        return false;
    }

    public static void eliminateFrame(ElementalBondsGameData elementalBondsGameData, Set<ElementalBondsBlock> elementalBondsBlockSet, ElementalBondsOperateType elementalBondsOperateType) {

        // 卡片不能消除框
        if (elementalBondsOperateType != ElementalBondsOperateType.MOVE && elementalBondsOperateType != ElementalBondsOperateType.USE_SKILL) {
            return;
        }

        Map<Integer, Map<Integer, ElementalBondsBlock>> frameMap = elementalBondsGameData.getFrameMap();

        List<ElementalBondsBlock> elementalBondsBlockList = new ArrayList<>();
        for (ElementalBondsBlock elementalBondsBlock : elementalBondsBlockSet) {
            if (frameMap.containsKey(elementalBondsBlock.getY()) && frameMap.get(elementalBondsBlock.getY()).containsKey(elementalBondsBlock.getX())) {
                ElementalBondsBlock frame = frameMap.get(elementalBondsBlock.getY()).get(elementalBondsBlock.getX());

                // 能否消除
                if (frame.getElementalBondsBlockType() == ElementalBondsBlockType.LUO_GU_XUAN_TIAN_DEL
                        || frame.getElementalBondsBlockType() == ElementalBondsBlockType.YI_BEN_WAN_LI
                        || frame.getElementalBondsBlockType() == ElementalBondsBlockType.DIAN_SHAN_LEI_MING) {
                    continue;
                }

                // 【正常逻辑】
                elementalBondsBlockList.add(frame);
                frameMap.get(frame.getY()).remove(frame.getX());

                // 消除后处理
                if (frame.getElementalBondsBlockType() == ElementalBondsBlockType.LUO_GU_XUAN_TIAN) {

                    ElementalBondsBlock clone = frame.clone();
                    clone.setElementalBondsBlockType(ElementalBondsBlockType.LUO_GU_XUAN_TIAN_DEL);
                    frameMap.get(frame.getY()).put(frame.getX(), clone);

                    ElementalBondsBlockEffect elementalBondsBlockEffect = getElementalBondsBlockEffect(frame);
                    elementalBondsBlock.addElementalBondsBlockEffect(elementalBondsBlockEffect);
                }
            }
        }

        elementalBondsBlockSet.addAll(elementalBondsBlockList);
    }

    private static ElementalBondsBlockEffect getElementalBondsBlockEffect(ElementalBondsBlock frame) {
        ElementalBondsBlockEffect elementalBondsBlockEffect = new ElementalBondsBlockEffect(ElementalBondsBlockEffectType.ADD_SCORE_HIDE);
        for (ElementalBondsBlockEffect tmpElementalBondsBlockEffect : frame.getElementalBondsBlockEffectList()) {
            if (tmpElementalBondsBlockEffect.getEffectType() == ElementalBondsBlockEffectType.LUO_GU_XUAN_TIAN) {
                elementalBondsBlockEffect.addEffectValue(tmpElementalBondsBlockEffect.getEffectValue());
            }
        }
        return elementalBondsBlockEffect;
    }

    /**
     * 消除的方块按颜色、相邻分组
     */
    public static Map<Integer, List<List<ElementalBondsBlock>>> groupEliminateSet(Set<ElementalBondsBlock> eliminateSet) {
        // 搞个空的盘面数据
        List<List<ElementalBondsBlock>> blockList = ElementalBondsBlockLogic.getEmptyBlockList(7, 7);
        // 填充一下
        for (ElementalBondsBlock elementalBondsBlock : eliminateSet) {
            if (elementalBondsBlock.getElementalBondsBlockType().getBigType().isBase()) {
                blockList.get(elementalBondsBlock.getY()).set(elementalBondsBlock.getX(), elementalBondsBlock);
            }
        }

        Map<Integer, List<List<ElementalBondsBlock>>> listListMap = new HashMap<>();

        for (ElementalBondsBlock elementalBondsBlock : eliminateSet) {
            // 跳过没颜色的
            if (elementalBondsBlock.getColor() == ElementalBondsColorType.NONE.getType()) {
                continue;
            }
            if (!canSameColorEliminate(blockList, elementalBondsBlock)) {
                continue;
            }

            listListMap.putIfAbsent(elementalBondsBlock.getColor(), new ArrayList<>());
            List<List<ElementalBondsBlock>> listList = listListMap.get(elementalBondsBlock.getColor());

            // 和该方块相邻的方块位于list的哪些下标
            List<Integer> adjoinIndexList = new ArrayList<>();
            for (int i = 0; i < listList.size(); i++) {
                List<ElementalBondsBlock> list = listList.get(i);
                for (ElementalBondsBlock tmpBlock : list) {
                    if (tmpBlock.getColor() == elementalBondsBlock.getColor() && ElementalBondsBlockLogic.adjoin(tmpBlock, elementalBondsBlock)) {
                        adjoinIndexList.add(i);
                        break;
                    }
                }
            }

            // 相邻的合并起来
            List<ElementalBondsBlock> list = new ArrayList<>();
            list.add(elementalBondsBlock);
            for (Integer index : adjoinIndexList) {
                list.addAll(listList.get(index));
            }
            for (int i = adjoinIndexList.size() - 1; i >= 0; i--) {
                listList.remove((int) adjoinIndexList.get(i));
            }
            listList.add(list);
        }

        // 排个序吧，按位置小到大
        for (List<List<ElementalBondsBlock>> listList : listListMap.values()) {
            for (List<ElementalBondsBlock> list : listList) {
                list.sort((o1, o2) -> {
                    if (o1.getY() != o2.getY()) {
                        return o1.getY() - o2.getY();
                    } else if (o1.getX() != o2.getX()) {
                        return o1.getX() - o2.getX();
                    } else {
                        return 0;
                    }
                });
            }
        }

        return listListMap;
    }

}
